package com.durian.base.rxhttp.ssl

import java.net.InetAddress
import java.net.Socket
import java.util.*
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager

/**
 * 说明：SSLSocketFactoryImpl
 * <p/>
 * 作者：Fanly
 * <p/>
 * 类型：Class
 * <p/>
 * 时间：2020/04/24 11:26
 * <p/>
 * 版本：version 1.0
 */
class SSLSocketFactoryImpl(x509TrustManager: X509TrustManager) : SSLSocketFactory(){

    private lateinit var defaultFactory: SSLSocketFactory
    private val protocols = mutableListOf<String>()
    private val cipherSuites = mutableListOf<String>()

    init {
        try {
            val sslContext = SSLContext.getInstance("TLS")
            sslContext.init(null, arrayOf(x509TrustManager),null)
            defaultFactory = sslContext.socketFactory

            protocols.clear()
            cipherSuites.clear()

            val socket = getDefault().createSocket() as SSLSocket
            socket.supportedProtocols.forEach {
                if (!it.toUpperCase().contains("SSL")){
                    protocols.add(it)
                }
            }
            if (Platform.get().sdkLessThan(21)){
                // choose known secure cipher suites
                val allowedCiphers = listOf( // TLS 1.2
                        "TLS_RSA_WITH_AES_256_GCM_SHA384",
                        "TLS_RSA_WITH_AES_128_GCM_SHA256",
                        "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                        "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                        "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                        "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                        "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256",  // maximum interoperability
                        "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
                        "TLS_RSA_WITH_AES_128_CBC_SHA",  // additionally
                        "TLS_RSA_WITH_AES_256_CBC_SHA",
                        "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
                        "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                        "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
                        "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA")

                val availableCiphers = listOf(*socket.supportedCipherSuites)

                // take all allowed ciphers that are available and put them into preferredCiphers
                // take all allowed ciphers that are available and put them into preferredCiphers
                val preferredCiphers = HashSet(allowedCiphers)
                preferredCiphers.retainAll(availableCiphers)
                /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus disabling
                 * ciphers which are enabled by default, but have become unsecure), but I guess for
                 * the security level of DAVdroid and maximum compatibility, disabling of insecure
                 * ciphers should be a server-side task */
                // add preferred ciphers to enabled ciphers
                /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus disabling
                 * ciphers which are enabled by default, but have become unsecure), but I guess for
                 * the security level of DAVdroid and maximum compatibility, disabling of insecure
                 * ciphers should be a server-side task */
                // add preferred ciphers to enabled ciphers
                preferredCiphers.addAll(HashSet(listOf(*socket.enabledCipherSuites)))
                cipherSuites.addAll(preferredCiphers)
            }
        }catch (e:Exception){
            e.printStackTrace()
        }
    }

    override fun getDefaultCipherSuites(): Array<String> {
        return cipherSuites.toTypedArray()
    }

    override fun createSocket(s: Socket?, host: String?, port: Int, autoClose: Boolean): Socket {
        val ssl = defaultFactory.createSocket(s, host, port, autoClose)
        if (ssl is SSLSocket) upgradeTLS(ssl)
        return ssl
    }

    override fun createSocket(host: String?, port: Int): Socket {
        val ssl = defaultFactory.createSocket(host, port)
        if (ssl is SSLSocket) upgradeTLS(ssl)
        return ssl
    }

    override fun createSocket(host: String?, port: Int, localHost: InetAddress?, localPort: Int): Socket {
        val ssl = defaultFactory.createSocket(host, port, localHost, localPort)
        if (ssl is SSLSocket) upgradeTLS(ssl)
        return ssl
    }

    override fun createSocket(host: InetAddress?, port: Int): Socket {
        val ssl = defaultFactory.createSocket(host, port)
        if (ssl is SSLSocket) upgradeTLS(ssl)
        return ssl
    }

    override fun createSocket(address: InetAddress?, port: Int, localAddress: InetAddress?, localPort: Int): Socket {
        val ssl = defaultFactory.createSocket(address, port, localAddress, localPort)
        if (ssl is SSLSocket) upgradeTLS(ssl)
        return ssl
    }

    override fun getSupportedCipherSuites(): Array<String> {
        return cipherSuites.toTypedArray()
    }

    private fun upgradeTLS(ssl:SSLSocket){
        if (protocols.isNotEmpty()){
            ssl.enabledProtocols = protocols.toTypedArray()
        }
        if (Platform.get().sdkLessThan(21) && cipherSuites.isNotEmpty()){
            ssl.enabledCipherSuites = cipherSuites.toTypedArray()
        }
    }

}